package eval

import (
	"showen/obj"
)

// StringToList converts a string to a list of characters
// (string->list string) essential procedure
func StringToList(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "string->list requires exactly 1 argument"}
	}

	if args[0].Type() != obj.STRING_OBJ {
		return &Error{Message: "string->list argument must be a string"}
	}

	str := args[0].(*obj.StringObject).Value
	runes := []rune(str)

	// Handle empty string case
	if len(runes) == 0 {
		return obj.EMPTY_LIST
	}

	// Build list from runes (in reverse order)
	var result obj.Object = obj.EMPTY_LIST
	for i := len(runes) - 1; i >= 0; i-- {
		charObj := &obj.CharacterObject{Value: runes[i]}
		result = &obj.PairObject{Car: charObj, Cdr: result}
	}

	return result
}

// ListToString converts a list of characters to a string
// (list->string chars) essential procedure
func ListToString(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "list->string requires exactly 1 argument"}
	}

	// Handle empty list case
	if args[0].Type() == obj.EMPTYLIST_OBJ || args[0].Type() == obj.NULL_OBJ {
		return &obj.StringObject{Value: ""}
	}

	current := args[0]

	// Extract characters from list
	var runes []rune
	for current.Type() == obj.PAIR_OBJ {
		pair := current.(*obj.PairObject)
		
		// Check if car is a character
		if charObj, ok := pair.Car.(*obj.CharacterObject); ok {
			runes = append(runes, charObj.Value)
		} else {
			return &Error{Message: "all elements of list must be characters"}
		}

		// Move to next element
		current = convertToObjObject(pair.Cdr)
	}

	// Check if we've reached the end of list properly
	if current.Type() != obj.EMPTYLIST_OBJ && current.Type() != obj.NULL_OBJ {
		return &Error{Message: "list->string argument must be a proper list"}
	}

	return &obj.StringObject{Value: string(runes)}
}

// StringCopy creates a copy of a string
// (string-copy string) procedure
func StringCopy(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "string-copy requires exactly 1 argument"}
	}

	if args[0].Type() != obj.STRING_OBJ {
		return &Error{Message: "string-copy argument must be a string"}
	}

	str := args[0].(*obj.StringObject).Value
	// In Go, strings are immutable, so returning the same string is safe
	return &obj.StringObject{Value: str}
}

// StringFill modifies a string by filling it with a character
// (string-fill! string char) procedure
func StringFill(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "string-fill! requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.STRING_OBJ {
		return &Error{Message: "string-fill! first argument must be a string"}
	}

	if args[1].Type() != obj.CHARACTER_OBJ {
		return &Error{Message: "string-fill! second argument must be a character"}
	}

	// Get the string and character
	strObj := args[0].(*obj.StringObject)
	charObj := args[1].(*obj.CharacterObject)

	// Get the rune to fill with
	fillChar := charObj.Value

	// Create a new string filled with the character
	// First we need to know the length of the original string
	runes := []rune(strObj.Value)
	newRunes := make([]rune, len(runes))
	for i := range newRunes {
		newRunes[i] = fillChar
	}

	// Update the string object
	strObj.Value = string(newRunes)

	// string-fill! returns an unspecified value, we'll use NULL_OBJECT
	return obj.NULL_OBJECT
}